package cs236369.webserver;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;

public class SocketReader implements ISocketReader {
	
	private int SO_TIMEOUT = 5000;
	private Socket mSocket;
	private DefaultHttpServerConnection mConnToClient;
	private HttpRequest mRequest;
	private String mUrl;
	private Map<String, String> mUrlParams;
	private String mPath;
	private IWebServerConfig mConfig;
	private ThreadPoolExecutor mRequestHandlerPool;
	private static Object mNotifier = new Object();
	
		
	public Object getNofier() {
		return mNotifier;
	}
	
	
	public SocketReader(Socket socket, IWebServerConfig config, ThreadPoolExecutor requestHandlerPool ) {
		mSocket = socket;
		mConfig = config;
		mRequestHandlerPool = requestHandlerPool;
	}

	public Map<String,String> getUrlParams(){
		return mUrlParams;
	}
	
	public String getPath(){
		return mPath;
	}
	
	public DefaultHttpServerConnection getConnToClient(){
		return mConnToClient;
	}
	
	@Override
	public void run() {
		WebServerLog.log(this, "run() started");
		try {	
			HttpParams params = new BasicHttpParams();
			
    		mConnToClient = new DefaultHttpServerConnection();
    		mConnToClient.bind(mSocket, params);
    		
    		mConnToClient.setSocketTimeout(SO_TIMEOUT);
    		
    		mRequest = mConnToClient.receiveRequestHeader();
					
			mUrl = mRequest.getRequestLine().getUri();
			mUrl = URLDecoder.decode(mUrl, "UTF-8");
			mUrlParams = new HashMap<String,String>();
			
			if (mRequest instanceof HttpEntityEnclosingRequest) {
				mConnToClient.receiveRequestEntity((HttpEntityEnclosingRequest) mRequest);
			
	            HttpEntity entity = ((HttpEntityEnclosingRequest) mRequest).getEntity();
	            StringBuilder contentStringBuilder = new StringBuilder();
	    		
	    		char[] buffer = new char[1024];
	    		Reader reader = null;
	    		    			
	    		int n;
	    		reader = new BufferedReader( new InputStreamReader(entity.getContent(), "UTF-8"));
	    		while ((n = reader.read(buffer)) != -1) {
	    			contentStringBuilder.append(buffer, 0, n);
	    		}
	    		
	    		
	    		String[] couples = contentStringBuilder.toString().split("&");
				for (String s : couples){
					String[] splitted = s.split("=");
					if (splitted.length == 2){
						mUrlParams.put(splitted[0], splitted[1]);
					}
				}		
	    		
	        }
	
					
			if (mUrl.contains("?")){
				String[] rawQuery = mUrl.split(Pattern.quote("?")); //get the query part
				String[] couples = rawQuery[1].split("&");
				for (String s : couples){
					String[] splitted = s.split("=");
					if (splitted.length == 2){
						mUrlParams.put(splitted[0], splitted[1]);
					}
				}		
			}
			
			
			mPath = mUrl.replaceFirst("http://[^/]*", "");
			mPath = mPath.replaceFirst(Pattern.quote("?") + ".*" ,"");
			
			
			if (mPath.equals("/")){
				mPath = mPath + mConfig.getWelcomeFile();
			}
			
			mPath = mPath.replace('/', File.separatorChar);
			mPath = mConfig.getBaseDirectory() + mPath;
			
			// Wait for the RequstHandlerQueue to be available
			for(;;) {
				try {
					mRequestHandlerPool.execute(new RequestHandler(this));
				} catch (RejectedExecutionException e) {
					WebServerLog.log(this, "RequstHandlerQueue is full, waiting for it...");
					synchronized(mNotifier) {
						mNotifier.wait();
					}
				}
				// if we got here, RequstHandlerQueue is available
				break;
			}
		} catch (Exception e) {
			System.err.println("ERROR: " + e.getMessage());
			e.printStackTrace();
			
			ErrorHandler eh = new ErrorHandler();

			HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 500, "Internal Server Error");
			RequestHandler.setDedaultHeaders(response);

			try {
				eh.handle(response, "Some Unexpected Internal Error Occured ", mConnToClient);
			} catch (HttpException e2) {
				System.err.println("ERROR: Cannot send error report to client " + e2.getMessage());
				e2.printStackTrace();
			} catch (IOException e2) {
				System.err.println("ERROR: Cannot send error report to client " + e2.getMessage());
				e2.printStackTrace();
			}
			try {
				mSocket.close();
			} catch (IOException e1) {
				System.err.println("ERROR: Cannot Close socket " + e1.getMessage());
				e1.printStackTrace();
			}
			
			
		}
		finally{
			WebServerLog.log(this, "run() ended");
		}

		
		
		
	}

	@Override
	public HttpRequest getHttpRequest() {
		return mRequest;
	}

	@Override
	public IWebServerConfig getConfig() {
		return mConfig;
	}
	

	
}
